Next | Prev | Up | Top | Contents | Index
Interacting With the Handler
The program process and the ULI handler synchronize their actions using two functions.
When the program cannot proceed without an interrupt, it calls ULI_sleep(), specifying
- the handle of the interrupt for which to wait
- the number of the semaphore to use for waiting
Typically only one process ever calls ULI_sleep() and it specifies waiting on semaphore 0. However, it is possible to have two or more processes that wait. For example, if the device can produce two distinct kinds of interrupts--normal and high-priority, perhaps--you could set up an independent process for each interrupt type. One would sleep on semaphore 0, the other on semaphore 1.
When an ULI handler is entered, it wakes up a program process by calling ULI_wakeup(), specifying the semaphore number to be posted. The handler must know which semaphore to post, based on the values it can read from the device or from program variables.
The ULI_sleep() call can terminate early, for example if a signal is sent to the process. The process that calls ULI_sleep() must test to find the reason the call returned--it is not necessarily because of an interrupt.
The ULI_wakeup() function can be called from normal code as well as from a ULI handler function. It could be used within any type of asynchronous callback function to wake up the program process.
The ULI_wakeup() call also specifies the handle of the interrupt. When you have multiple interrupting devices, you have the following design choices:
- You can have one child process waiting on the handler for each device. In this case, each ULI handler specifies its own handle to ULI_wakeup().
- You can have a single process that waits on any interrupt. In this case, the main program specifies the handle of one particular interrupt to ULI_sleep(), and every ULI handler specifies that same handle to ULI_wakeup().
Achieving Mutual Exclusion
The program can gain exclusive use of global variables with a call to ULI_block_intr(). This function does not block receipt of the hardware interrupt, but does block the call to the ULI handler. Until the program process calls ULI_unblock_intr(), it can test and update global variables without danger of a race condition. This period of time should be as short as possible, because it extends the interrupt latency time. If more than one hardware interrupt occurs while the ULI handler is blocked, it will be called for only the last-received interrupt.
There are other techniques for safe handling of shared global variables besides blocking interrupts. One important, and little-known, set of tools is the test_and_set() group of functions documented in the test_and_set(3) reference page. These instructions use the Load Linked and Store Conditional instructions of the MIPS instruction set to safely update global variables in various ways.
Next | Prev | Up | Top | Contents | Index